﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace MecabDotNet
{
    /// <summary>
    /// Mecabラッパ
    /// </summary>
    public class Mecab : _Mecab
    {
        /// <summary>
        /// １つの形態素を出力、デフォルトは空文字
        /// </summary>
        private string node = "%m\\t%F,[0,1,2,3,4,5,6,7,8]\\n";

        /// <summary>
        /// １つの未知語形態素解析を出力、デフォルトはnodeと同一フォーマット
        /// </summary>
        private string unk = "%m\\t%F,[0,1,2,3,4,5,6],%m,%m\\n";

        /// <summary>
        /// 形態素解析の結果に先立って出力（header的役割）、デフォルトは空文字
        /// </summary>
        private string bos = "";

        /// <summary>
        /// 形態素解析の結果の後に出力（footer的役割）、デフォルトは"EOS\n";
        /// </summary>
        private string eos = "";

        /// <summary>
        /// １つの形態素を出力、デフォルトは空文字
        /// </summary>
        public string Node
        {
            get { return this.node; }
        }

        /// <summary>
        /// １つの未知語形態素解析を出力、デフォルトはnodeと同一フォーマット
        /// </summary>
        public string Unk
        {
            get { return this.unk; }
        }

        /// <summary>
        /// 形態素解析の結果に先立って出力（header的役割）、デフォルトは空文字
        /// </summary>
        public string Bos
        {
            get { return this.bos; }
        }

        /// <summary>
        /// 形態素解析の結果の後に出力（footer的役割）、デフォルトは"EOS\n";
        /// </summary>
        public string Eos
        {
            get { return this.eos; }
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Mecab()
        {
            this.ptrMecab = this._mecab_new2(string.Format(
                "{0} {1} {2} {3}",
                (this.node != "" ? string.Format("--node-format={0}", this.node) : ""),
                (this.unk != "" ? string.Format("--unk-format={0}", this.unk) : ""),
                (this.bos != "" ? string.Format("--bos-format={0}", this.bos) : ""),
                (this.eos != "" ? string.Format("--eos-format={0}", this.eos) : "")
            ));

        }
        //        public Mecab() : this("") { }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="arg"></param>
        public Mecab(string arg)
        {
            this.ptrMecab = this._mecab_new2(arg);
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="node"></param>
        /// <param name="unk"></param>
        /// <param name="bos"></param>
        /// <param name="eos"></param>
        public Mecab(string node, string unk, string bos, string eos)
        {
            this.node = node;
            this.unk = (unk == "" ? node : unk);
            this.bos = bos;
            this.eos = eos;

            this.ptrMecab = this._mecab_new2(string.Format(
                "{0} {1} {2} {3}",
                (node != "" ? string.Format("--node-format={0}", node) : ""),
                (unk != "" ? string.Format("--unk-format={0}", unk) : ""),
                (bos != "" ? string.Format("--bos-format={0}", bos) : ""),
                (eos != "" ? string.Format("--eos-format={0}", eos) : "")
            ));
        }

        /// <summary>
        /// mecab の version を文字列として取得します.
        /// </summary>
        /// <returns></returns>
        public string mecab_version
        {
            get { return this._mecab_version(); }
        }

        /// <summary>
        /// エラーの内容を返します。
        /// </summary>
        /// <returns></returns>
        public string mecab_strerror()
        {
            return this._mecab_strerror();
        }

        public string mecab_sparse_tostr(string str)
        {
            return this._mecab_sparse_tostr(str);
        }

        public string mecab_sparse_tostr2(int n, string str, int length)
        {
            return this._mecab_nbest_sparse_tostr2(n, str, length);
        }

        public string mecab_sparse_tostr3(string strSrc, out string strDst, int olen)
        {
            return this._mecab_sparse_tostr3(strSrc, out strDst, olen);
        }

        public string mecab_nbest_sparse_tostr(int n, string strSrc)
        {
            return this._mecab_nbest_sparse_tostr(n, strSrc);
        }

        public string mecab_nbest_sparse_tostr2(int n, string strSrc, int length)
        {
            return this._mecab_nbest_sparse_tostr2(n, strSrc, length);
        }

        public string mecab_nbest_sparse_tostr3(
            int n,
            string strSrc,
            int length,
            out string strDst,
            int olen)
        {
            return this._mecab_nbest_sparse_tostr3(
                n,
                strSrc,
                length,
                out strDst,
                olen
            );
        }

        public int mecab_nbest_init(string strSrc)
        {
            return this._mecab_nbest_init(strSrc);
        }

        public int mecab_nbest_init2(string strSrc, int length)
        {
            return this._mecab_nbest_init2(strSrc, length);
        }

        public string mecab_nbest_next_tostr()
        {
            return this._mecab_nbest_next_tostr();
        }

        public string mecab_nbest_nbest_tostr2(out string strDst, int olen)
        {
            return this._mecab_nbest_next_tostr2(out strDst, olen);
        }

        public void mecab_destroy()
        {
            this._mecab_destroy();
        }

    }

    /// <summary>
    /// このクラスのインスタンスを直接利用しないでください。
    /// </summary>
    public class _Mecab : IDisposable
    {
        protected IntPtr ptrMecab;

        //// path/to/libmecab.dll | libmecab.so
        ////環境に合わせてmecab本体ｈのパスを変更してください。
        //protected const string libmecab = @"C:\Program Files\MeCab\bin\libmecab.dll";

        /*
        /// <summary>
        /// コンストラクタ.
        /// mecab のインスタンスを生成します.
        /// 引数には, C 言語の, main 関数で使用される argc, argv スタイルの引数を与えます.
        /// この引数は, mecab コマンドと同じ方法で処理されます.
        /// 成功すれば, mecab_t 型のポインタが返ってきます. このポインタを通して解析 を行います. 失敗すれば NULL が返ってきます.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        [DllImport("libmecab")]
        private extern IntPtr mecab_new(int argc, string arg);
         */

        /// <summary>
        /// コンストラクタ
        /// mecab のインスタンスを生成します.
        /// 引数には, 一つの文字列として表現したパラメータを与えます.
        /// 成功すれば, mecab_t 型のポインタが返ってきます. このポインタを通して解析を行います.
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        [DllImport("libmecab")]
        private static extern IntPtr mecab_new2(string arg);

        protected IntPtr _mecab_new2(string arg)
        {
            return mecab_new2(arg);
        }

        [DllImport("libmecab")]
        private static extern IntPtr mecab_version();

        protected string _mecab_version()
        {
            IntPtr s = mecab_version();
            if (s != null)
            {
                return Marshal.PtrToStringAnsi(s);
            }
            else
            {
                return null;
            }
        }

        [DllImport("libmecab")]
        private static extern IntPtr mecab_strerror(IntPtr ptrMecab);

        protected string _mecab_strerror()
        {
            IntPtr s = mecab_strerror(this.ptrMecab);
            if (s != null)
            {
                return Marshal.PtrToStringAnsi(s);
            }
            else
            {
                return null;
            }
        }

        [DllImport("libmecab")]
        private static extern IntPtr mecab_sparse_tostr(IntPtr ptrMecab, string str);
        protected string _mecab_sparse_tostr(string str)
        {
            IntPtr s = mecab_sparse_tostr(this.ptrMecab, str);
            if (s != null)
            {
                return Marshal.PtrToStringAnsi(s);
            }
            else
            {
                return null;
            }
        }

        [DllImport("libmecab")]
        private static extern IntPtr mecab_sparse_tostr2(IntPtr ptrMecab, string str, int length);
        protected string _mecab_sparse_tostr2(string str, int length)
        {
            IntPtr s = mecab_sparse_tostr2(this.ptrMecab, str, length);
            if (s != null)
            {
                return Marshal.PtrToStringAnsi(s);
            }
            else
            {
                return null;
            }
        }

        [DllImport("libmecab")]
        private static extern string mecab_sparse_tostr3(
            IntPtr ptrMecab,
            string strSrc,
            out string strDst,
            int olen
        );
        protected string _mecab_sparse_tostr3(
            string strSrc,
            out string strDst,
            int olen)
        {
            return mecab_sparse_tostr3(
                this.ptrMecab,
                strSrc,
                out strDst,
                olen
            );
        }

        [DllImport("libmecab")]
        private static extern IntPtr mecab_nbest_sparse_tostr(IntPtr ptrMecab, int n, string str);
        protected string _mecab_nbest_sparse_tostr(int n, string str)
        {
            var s = mecab_nbest_sparse_tostr(this.ptrMecab, n, str);
            if (s != null)
            {
                return Marshal.PtrToStringAnsi(s);
            }
            else
            {
                return null;
            }
        }

        [DllImport("libmecab")]
        private static extern IntPtr mecab_nbest_sparse_tostr2(
            IntPtr ptrMecab,
            int n,
            string str,
            int length
        );
        protected string _mecab_nbest_sparse_tostr2(int n, string str, int length)
        {
            var s = mecab_nbest_sparse_tostr2(this.ptrMecab, n, str, length);
            if (s != null)
            {
                return Marshal.PtrToStringAnsi(s);
            }
            else
            {
                return null;
            }
        }

        [DllImport("libmecab")]
        private static extern string mecab_nbest_sparse_tostr3(
            IntPtr ptrMecab,
            int n,
            string strSrc,
            int length,
            out string strDst,
            int olen
        );
        protected string _mecab_nbest_sparse_tostr3(
            int n,
            string strSrc,
            int length,
            out string strDst,
            int olen)
        {
            return mecab_nbest_sparse_tostr3(
                this.ptrMecab,
                n,
                strSrc,
                length,
                out strDst,
                olen
            );
        }

        [DllImport("libmecab")]
        private static extern int mecab_nbest_init(IntPtr ptrMecab, string strSrc);
        protected int _mecab_nbest_init(string strSrc)
        {
            return mecab_nbest_init(this.ptrMecab, strSrc);
        }

        [DllImport("libmecab")]
        private static extern int mecab_nbest_init2(IntPtr ptrMecab, string strSrc, int length);
        protected int _mecab_nbest_init2(string strSrc, int length)
        {
            return mecab_nbest_init2(this.ptrMecab, strSrc, length);
        }

        [DllImport("libmecab")]
        private static extern string mecab_nbest_next_tostr(IntPtr ptrMecab);
        protected string _mecab_nbest_next_tostr()
        {
            return mecab_nbest_next_tostr(this.ptrMecab);
        }

        [DllImport("libmecab")]
        private static extern IntPtr mecab_nbest_next_tostr2(IntPtr ptrMecab, out string strDst, int olen);
        protected string _mecab_nbest_next_tostr2(out string strDst, int olen)
        {
            var s = mecab_nbest_next_tostr2(this.ptrMecab, out strDst, olen);
            if (s != null)
            {
                return Marshal.PtrToStringAnsi(s);
            }
            else
            {
                return null;
            }

        }

        [DllImport("libmecab")]
        private static extern void mecab_destroy(IntPtr ptrMecab);
        protected void _mecab_destroy()
        {
            mecab_destroy(this.ptrMecab);
        }



        public void Dispose()
        {
            GC.SuppressFinalize(this);
        }
    }
}
